package com.internetCafes.spms.web.certificate.bizservice.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.internetCafes.spms.common.CommonConstants;
import com.internetCafes.spms.common.utils.ShiroUtils;
import com.internetCafes.spms.core.exception.RRException;
import com.internetCafes.spms.core.exception.RRExceptionCodeEnum;
import com.internetCafes.spms.web.certificate.bizservice.CertTransactionBizService;
import com.internetCafes.spms.web.certificate.common.cont.CertConst;
import com.internetCafes.spms.web.certificate.common.cont.CertTransactionConst;
import com.internetCafes.spms.web.certificate.entity.CertiTransactionInfo;
import com.internetCafes.spms.web.certificate.entity.CertificateInfo;
import com.internetCafes.spms.web.certificate.entity.EmployeeInfo;
import com.internetCafes.spms.web.certificate.model.certificate.req.CertTransactionInfoReq;
import com.internetCafes.spms.web.certificate.service.CertiTransactionInfoService;
import com.internetCafes.spms.web.certificate.service.CertificateInfoService;
import com.internetCafes.spms.web.certificate.service.EmployeeInfoService;
import com.internetCafes.spms.web.customer.common.cont.CommonConst;
import com.internetCafes.spms.web.customer.service.user.IEntUserInfoService;
import com.internetCafes.spms.web.sys.model.UserInfo;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 证书流转业务处理实现
 *
 * @author HHKJ-SERVER-ZJ 2022/6/14 15:07
 */
@Service
public class CertTransactionBizServiceImpl implements CertTransactionBizService {

    @Autowired
    private CertificateInfoService certService;
    @Autowired
    private CertiTransactionInfoService transactionService;
    @Autowired
    private EmployeeInfoService employeeService;
    @Autowired
    private IEntUserInfoService userInfoService;


    @Override
    public void save(CertTransactionInfoReq req) {
        // 必填字段校验
        if (ObjectUtil.isNull(req.getCertificateId()) || ObjectUtil.isNull(req.getType())) {
            throw new RRException(RRExceptionCodeEnum.PARAM_ERROR);
        }
        UserInfo userInfo = ShiroUtils.getUserEntity();
        // 获取证书情况
        CertificateInfo certInfoPo = certService.findById(req.getCertificateId());
        Date nowDate = new Date();
        // 基础信息配置
        req.setCreateBy(userInfo.getId());
        req.setCreateTime(nowDate);
        req.setTenantId(userInfo.getTenantId());


        transactionService.save(req);

        if (CertTransactionConst.Type.AFFILIATION_OUT.getId().equals(req.getType())
                || CertTransactionConst.Type.BORROW_OUT.getId().equals(req.getType())) {
            if (certInfoPo.getDisable()) {
                throw new RRException(RRExceptionCodeEnum.CERT_CERTIFICATE_OVERDUE);
            }
            req.setFromDeptId(certInfoPo.getKeepDeptId());
            req.setFromDeptName(certInfoPo.getKeepDeptName());
            req.setFromUserId(certInfoPo.getKeepUserId());
            req.setFromUserName(certInfoPo.getKeepUserName());
            req.setFromUserMobile(certInfoPo.getKeepContactMobile());
            req.setStatusTag(CertTransactionConst.StatusTag.IN_HAND.getId());
            if (ObjectUtil.equals(CertTransactionConst.BorrowCandidatesSources.OTHER.getId(), certInfoPo.getBorrowCandidatesSources())) {
                certInfoPo.setBorrowCandidatesSources(CertTransactionConst.BorrowCandidatesSources.PERSONAL.getId());
                certInfoPo.setBorrowOrgName(userInfo.getName() + "(" + CertTransactionConst.BorrowCandidatesSources.PERSONAL.getSourceName() + ")");
            }
            // 借出
            if (CertTransactionConst.Type.BORROW_OUT.getId().equals(req.getType())) {
                EmployeeInfo employeePo = employeeService.findById(req.getToUserId());
                req.setToUserName(employeePo.getName());
                req.setToDeptName(employeePo.getDeptName());

            }

        }
        if (CertTransactionConst.Type.BORROW_OUT_RETURN.getId().equals(req.getType())
                || CertTransactionConst.Type.AFFILIATION_OUT_RETURN.getId().equals(req.getType())) {
        }

    }

    /**
     * 挂出
     *
     * @param req 流转请求参数
     */
    @Override
    @Transactional
    public void affiliationOut(CertTransactionInfoReq req) {
        /*
         * 注释 by Zj 2022 06 15
         * 1- 在流转记录中记录，证书的原单位/原保管人/部门信息/保管人手机号 信息
         * 2- 修改证书信息
         *      如果证书来源是信息 不是 null  或者 值不为 2 的话，需要将借入信息设置为当前用户
         *      PS： 由于历史需求无法追溯： 这里猜测，如果不是这两种情况的时候，证书是个人的，
         *           证书挂出/借出的时候 就是由当前用户借出，所以，借入信息保存为当前用户个人
         */
        if (ObjectUtil.isNull(req.getCertInfoPo())) {
            throw new RRException(RRExceptionCodeEnum.PARAM_ERROR);
        }
        UserInfo userInfo = ShiroUtils.getUserEntity();
        Date nowDate = new Date();
        CertificateInfo certInfoPo = req.getCertInfoPo();
        // 基础信息配置
        req.setType(CertTransactionConst.Type.AFFILIATION_OUT.getId());
        req.setCreateBy(userInfo.getId());
        req.setCreateTime(nowDate);
        req.setTenantId(userInfo.getTenantId());

        req.setFromDeptId(certInfoPo.getKeepDeptId());
        req.setFromDeptName(certInfoPo.getKeepDeptName());
        req.setFromUserId(certInfoPo.getKeepUserId());
        req.setFromUserName(certInfoPo.getKeepUserName());
        req.setFromUserMobile(certInfoPo.getKeepContactMobile());
        req.setStatusTag(CertTransactionConst.StatusTag.IN_HAND.getId());

        if (ObjectUtil.equals(
                CertTransactionConst.BorrowCandidatesSources.OTHER.getId(),
                certInfoPo.getBorrowCandidatesSources())) {
            certInfoPo.setBorrowCandidatesSources(CertTransactionConst.BorrowCandidatesSources.PERSONAL.getId());
            certInfoPo.setBorrowOrgName(
                    userInfo.getName() + "(" +
                            CertTransactionConst.BorrowCandidatesSources.PERSONAL.getSourceName() + ")");
        }
        certInfoPo.setStatus(CertConst.Status.BORROW_OUT.getId());

        transactionService.save(req);
        // CertiTransactionInfoVo transactionVoReq = BeanUtil.toBean(req, CertiTransactionInfoVo.class);
        // transactionService.saveResource(transactionVoReq);
        certService.update(certInfoPo);
    }

    /**
     * 借出
     *
     * @param req 流转请求参数
     */
    @Override
    @Transactional
    public void borrowOut(CertTransactionInfoReq req) {
        /*
         * 证书借出操作
         * 1- 将证书借给单位/人 用于项目或其他情况
         * 1- 押证/押人 判断，对是否还能押证，和借出是否支持进行判断
         *      押人：证书进行押人的时候，这个人 对应的证书都不支持借出
         *      押证：证书不支持二次借出
         */
        if (ObjectUtil.isNull(req.getCertInfoPo())) {
            throw new RRException(RRExceptionCodeEnum.PARAM_ERROR);
        }
        CertificateInfo certInfoPo = req.getCertInfoPo();
        HashMap<String, Object> params;
        // 押人操作校验 修改证书信息 直接将押证押人标签
        if (CommonConst.FlagEnum.IS.getId().equals(req.getDetaineeFlag())) {
            // 查询该证书员工 手下的所有证书信息
            params = new HashMap<>();
            params.put("employeeId", certInfoPo.getEmployeeId());
            params.put("borrowOutFlag", CommonConst.FlagEnum.IS.getFlag());
            List<CertificateInfo> certPoList = certService.query(params);
            // 查询对应的人员的证书是否有借出的，如果有借出的证书，不支持押人
            if (CollectionUtil.isNotEmpty(certPoList)) {
                throw new RRException(RRExceptionCodeEnum.CERT_CERTIFICATE_UNABLE_DETAINEE);
            }
        }

        // 是否支持押证 校验
        if (CommonConst.FlagEnum.IS.getId().equals(req.getWarrantFlag())) {
            if (CommonConst.FlagEnum.IS.getId().equals(certInfoPo.getBorrowOutFlag())) {
                throw new RRException(RRExceptionCodeEnum.CERT_CERTIFICATE_UNABLE_WARRANT);
            }
        }

        // 是否能借出 校验
        if (CommonConst.FlagEnum.IS.getId().equals(certInfoPo.getWarrantFlag())) {
            throw new RRException(RRExceptionCodeEnum.CERT_CERTIFICATE_UNABLE_WARRANT);
        }

        // 借出项目中不能重复
        if (ObjectUtil.isNotNull(req.getProjectId())) {
            params = new HashMap<>();
            params.put("certificateId", certInfoPo.getId());
            params.put("type", CertTransactionConst.Type.BORROW_OUT.getId());
            params.put("projectId", req.getProjectId());
            params.put("statusTag", CertTransactionConst.StatusTag.IN_HAND.getId());
            List<CertiTransactionInfo> borrowOutOfProjectPoList = transactionService.query(params);
            if (CollectionUtils.isNotEmpty(borrowOutOfProjectPoList)) {
                throw new RRException(RRExceptionCodeEnum.CERT_CERTIFICATE_HAS_BORROW_OUT_THIS_PROJECT);
            }
        }

        // 通过校验，进行借出
        EmployeeInfo certEmployeePo = employeeService.findById(certInfoPo.getEmployeeId());
        if (ObjectUtil.isNull(certEmployeePo)) {
            throw new RRException(RRExceptionCodeEnum.EMPLOYEE_IS_NOT_EXIT);
        }

        UserInfo userInfo = ShiroUtils.getUserEntity();
        req.setType(CertTransactionConst.Type.BORROW_OUT.getId());
        Date nowDate = new Date();
        // 基础信息配置
        req.setCreateBy(userInfo.getId());
        req.setCreateTime(nowDate);
        req.setTenantId(userInfo.getTenantId());

        // 填写证书来源信息
        req.setFromDeptId(certInfoPo.getKeepDeptId());
        req.setFromDeptName(certInfoPo.getKeepDeptName());
        req.setFromUserId(certInfoPo.getKeepUserId());
        req.setFromUserName(certInfoPo.getKeepUserName());
        req.setFromUserMobile(certInfoPo.getKeepContactMobile());
        // 提供操作为进行中
        req.setStatusTag(CertTransactionConst.StatusTag.IN_HAND.getId());

        if (ObjectUtil.equals(CertTransactionConst.BorrowCandidatesSources.OTHER.getId(), certInfoPo.getBorrowCandidatesSources())) {
            certInfoPo.setBorrowCandidatesSources(CertTransactionConst.BorrowCandidatesSources.PERSONAL.getId());
            certInfoPo.setBorrowOrgName(userInfo.getName() + "(" + CertTransactionConst.BorrowCandidatesSources.PERSONAL.getSourceName() + ")");
        }

        // 填写借出给人的信息
        EmployeeInfo toEmployeeInfoPo = employeeService.findById(req.getToUserId());
        req.setToUserName(toEmployeeInfoPo.getName());
        req.setToDeptName(toEmployeeInfoPo.getDeptName());

        // 填写借出相关信息 默认是项目借出
        CertConst.Status usageType = CertConst.Status.getById(req.getUsage());
        req.setRemark("[" + usageType.getStatusName() + "] " + req.getRemark());
        certInfoPo.setKeepDeptId(req.getToDeptId());
        certInfoPo.setKeepDeptName(toEmployeeInfoPo.getDeptName());
        certInfoPo.setKeepUserId(req.getToUserId());
        certInfoPo.setKeepUserName(toEmployeeInfoPo.getName());
        certInfoPo.setKeepContactMobile(req.getToUserMobile());
        // 设置证书状态
        // 证书借出状态修改 如果证书状态不为挂出 ， 修改状态，两种情况同时存在的时候，保持挂出状态
        if (!CertConst.Status.BORROW_OUT.getId().equals(certInfoPo.getStatus())) {
            certInfoPo.setStatus(usageType.getId());
        }
        certInfoPo.setBorrowOutFlag(CommonConst.FlagEnum.IS.getId());
        certInfoPo.setWarrantFlag(req.getWarrantFlag());

        transactionService.save(req);
        // CertiTransactionInfoVo transactionVoReq = BeanUtil.toBean(req, CertiTransactionInfoVo.class);
        // transactionService.saveResource(transactionVoReq);
        certService.update(certInfoPo);
    }

    @Override
    @Transactional
    public void affiliationOutReturn(CertTransactionInfoReq req) {
        /*
         * 收回：
         *   1-将挂出的证书收回到自己这里进行管理
         *   2-需要修改 结束证书挂出记录的状态
         *   3-状态恢复的时候需要注意，证书是否还在借出状态，需要修改证书最后状态到最后的借出记录中
         */
        if (ObjectUtil.isNull(req.getCertInfoPo())) {
            throw new RRException(RRExceptionCodeEnum.PARAM_ERROR);
        }
        CertificateInfo certInfoPo = req.getCertInfoPo();
        UserInfo userInfo = ShiroUtils.getUserEntity();
        Date nowDate = new Date();
        req.setType(CertTransactionConst.Type.AFFILIATION_OUT_RETURN.getId());
        // 基础信息配置
        req.setCreateBy(userInfo.getId());
        req.setCreateTime(nowDate);
        req.setTenantId(userInfo.getTenantId());

        // 获取最后一条挂出记录 由于有些事旧数据 兼容的情况下 需要对对应数据做出修复兼容
        Map<String, Object> params = new HashMap<>();
        params.put("certificateId", certInfoPo.getId());
        params.put("type", CertTransactionConst.Type.AFFILIATION_OUT.getId());
        //params.put("statusTag", CertTransactionConst.StatusTag.IN_HAND.getId());
        CertiTransactionInfo affiliationOutInfoPo = transactionService.findFirst(params);

        if (ObjectUtil.isNull(affiliationOutInfoPo)) {
            throw new RRException(RRExceptionCodeEnum.CERT_CERTIFICATE_NO_AFFILIATION_RECORD);
        }
        // 默认收回时间
        if (ObjectUtil.isNull(req.getTransactionTime())) {
            req.setTransactionTime(nowDate);
            req.setTransactionEndTime(nowDate);
        }
        // 只有 是挂出/借出人，或者挂出/借出人的上级 才能进行操作|证书的录入人
        if (!userInfo.getId().equals(certInfoPo.getCreateBy())
                && !userInfoService.isLowerLevelUserIdOfManagerUser(affiliationOutInfoPo.getCreateBy(), userInfo)) {
            throw new RRException(RRExceptionCodeEnum.NO_AUTHORITY);
        }
        // 挂出记录中 需要修改的数据
        affiliationOutInfoPo.setStatusTag(CertTransactionConst.StatusTag.COMPLETION.getId());
        // 将挂出记录复制到还回中
        req.copyAffiliationOutInfoToReturn(affiliationOutInfoPo);

        // 修改证书的状态
        if (CommonConst.FlagEnum.IS.getId().equals(certInfoPo.getBorrowOutFlag())) {
            // 获取最后一条借出未还回的记录，修改证书的状态
            params = new HashMap<>();
            params.put("certificateId", certInfoPo.getId());
            params.put("type", CertTransactionConst.Type.BORROW_OUT.getId());
            List<CertiTransactionInfo> borrowOutInfoPoList = transactionService.query(params);
            // 倒序重新排序一次
            borrowOutInfoPoList = CollectionUtil.sort(borrowOutInfoPoList, (po1, po2) -> (int) (po2.getId() - po1.getId()));
            if (CollectionUtil.isEmpty(borrowOutInfoPoList)) {
                // 错误数据矫正
                certInfoPo.setStatus(CommonConstants.CertificateStatus.NO_USE);
                certInfoPo.setWarrantFlag(CommonConst.FlagEnum.NOT.getId());
                certInfoPo.setBorrowOutFlag(CommonConst.FlagEnum.NOT.getId());
            } else {
                CertiTransactionInfo borrowOutInfoPoLast = null;
                for (CertiTransactionInfo borrowOutInfoPo : borrowOutInfoPoList) {
                    if (ObjectUtil.isNull(borrowOutInfoPo.getStatusTag()) ||
                            CertTransactionConst.StatusTag.IN_HAND.getId().equals(borrowOutInfoPo.getStatusTag())) {
                        borrowOutInfoPoLast = borrowOutInfoPo;
                        break;
                    }
                }
                if (ObjectUtil.isNotNull(borrowOutInfoPoLast)) {
                    // 旧数据修复
                    if (ObjectUtil.isNull(borrowOutInfoPoLast.getStatusTag())) {
                        borrowOutInfoPoLast.setStatusTag(CertTransactionConst.StatusTag.IN_HAND.getId());
                        transactionService.update(borrowOutInfoPoLast);
                    }
                    certInfoPo.setStatus(CertConst.Status.getById(borrowOutInfoPoLast.getUsage()).getId());
                } else {
                    // 错误数据矫正
                    certInfoPo.setStatus(CertConst.Status.NO_USE.getId());
                    certInfoPo.setWarrantFlag(CommonConst.FlagEnum.NOT.getId());
                    certInfoPo.setBorrowOutFlag(CommonConst.FlagEnum.NOT.getId());
                }
            }
        } else {
            certInfoPo.setStatus(CertConst.Status.NO_USE.getId());
        }
        transactionService.save(req);
        transactionService.update(affiliationOutInfoPo);
        certService.update(certInfoPo);
    }

    @Override
    @Transactional
    public void borrowOutReturn(CertTransactionInfoReq req) {
        /*
         * 证书还回
         *  1-借出的证书收回到自己手中
         *  2-因为借出可以是多条记录同时进行，所以需要对应到借出的流转数据上
         *  3-还回后，需要重新确定证书的状态
         */
        if (ObjectUtil.isNull(req.getCertInfoPo())) {
            throw new RRException(RRExceptionCodeEnum.PARAM_ERROR);
        }
        CertificateInfo certInfoPo = req.getCertInfoPo();
        if (ObjectUtil.isNull(req.getReturnFromId())) {
            throw new RRException(RRExceptionCodeEnum.PARAM_ERROR);
        }
        CertiTransactionInfo borrowOutPo = transactionService.findById(req.getReturnFromId());
        if (ObjectUtil.isNull(borrowOutPo)) {
            throw new RRException(RRExceptionCodeEnum.DATA_NOT_EXIST);
        }

        UserInfo userInfo = ShiroUtils.getUserEntity();
        Date nowDate = new Date();
        req.setType(CertTransactionConst.Type.BORROW_OUT_RETURN.getId());

        // 基础信息配置
        req.setCreateBy(userInfo.getId());
        req.setCreateTime(nowDate);
        req.setTenantId(userInfo.getTenantId());
        // 默认收回时间
        if (ObjectUtil.isNull(req.getTransactionTime())) {
            req.setTransactionTime(nowDate);
            req.setTransactionEndTime(nowDate);
        }

        // 只有 是挂出/借出人，或者挂出/借出人的上级 才能进行操作
        if (!userInfo.getId().equals(certInfoPo.getCreateBy())
                && !userInfoService.isLowerLevelUserIdOfManagerUser(borrowOutPo.getCreateBy(), userInfo)) {
            throw new RRException(RRExceptionCodeEnum.NO_AUTHORITY);
        }

        // 验证对应记录是否返回
        if (CertTransactionConst.StatusTag.COMPLETION.getId().equals(borrowOutPo.getStatusTag())) {
            throw new RRException(RRExceptionCodeEnum.CERT_CERTIFICATE_BORROW_OUT_HAS_RETURN);
        }

        // 修改对应借出记录信息
        borrowOutPo.setStatusTag(CertTransactionConst.StatusTag.COMPLETION.getId());
        borrowOutPo.setTransactionEndTime(req.getTransactionEndTime());
        if (ObjectUtil.isNull(borrowOutPo.getStatusTag())) {
            // 旧数据自修复
            borrowOutPo.setDetaineeFlag(CommonConst.FlagEnum.NOT.getId());
            borrowOutPo.setWarrantFlag(CommonConst.FlagEnum.IS.getId());
        }

        // 创建当前流转操作信息
        req.copyBorrowOutInfoToReturn(borrowOutPo);

        // 更新当前证书最后的状态
        // 修改证书的状态
        Integer certFinallyStatus;

        if (CommonConst.FlagEnum.IS.getId().equals(certInfoPo.getBorrowOutFlag())) {
            // 获取最后一条借出未还回的记录，修改证书的状态
            Map<String, Object> params = new HashMap<>();
            params.put("certificateId", certInfoPo.getId());
            params.put("type", CertTransactionConst.Type.BORROW_OUT.getId());

            // 将本次还回的记录排除在外
            List<CertiTransactionInfo> borrowOutInfoPoList = transactionService.query(params)
                    .stream().filter(po -> !req.getReturnFromId().equals(po.getId())).collect(Collectors.toList());
            // 倒序重新排序一次
            borrowOutInfoPoList = CollectionUtil.sort(borrowOutInfoPoList, (po1, po2) -> (int) (po2.getId() - po1.getId()));
            if (CollectionUtil.isEmpty(borrowOutInfoPoList)) {
                // 错误数据矫正
                certFinallyStatus = CommonConstants.CertificateStatus.NO_USE;
                certInfoPo.setWarrantFlag(CommonConst.FlagEnum.NOT.getId());
                certInfoPo.setBorrowOutFlag(CommonConst.FlagEnum.NOT.getId());
            } else {
                CertiTransactionInfo borrowOutInfoPoLast = null;
                for (CertiTransactionInfo borrowOutInfoPo : borrowOutInfoPoList) {
                    if (ObjectUtil.isNull(borrowOutInfoPo.getStatusTag()) ||
                            CertTransactionConst.StatusTag.IN_HAND.getId().equals(borrowOutInfoPo.getStatusTag())) {
                        borrowOutInfoPoLast = borrowOutInfoPo;
                        break;
                    }
                }
                if (ObjectUtil.isNotNull(borrowOutInfoPoLast)) {
                    // 旧数据修复
                    if (ObjectUtil.isNull(borrowOutInfoPoLast.getStatusTag())) {
                        borrowOutInfoPoLast.setStatusTag(CertTransactionConst.StatusTag.IN_HAND.getId());
                        borrowOutInfoPoLast.setDetaineeFlag(CommonConst.FlagEnum.NOT.getId());
                        borrowOutInfoPoLast.setWarrantFlag(CommonConst.FlagEnum.IS.getId());
                        transactionService.update(borrowOutInfoPoLast);
                    }
                    certFinallyStatus = CertConst.Status.getById(borrowOutInfoPoLast.getUsage()).getId();
                } else {
                    // 错误数据矫正
                    certFinallyStatus = CertConst.Status.NO_USE.getId();
                    certInfoPo.setWarrantFlag(CommonConst.FlagEnum.NOT.getId());
                    certInfoPo.setBorrowOutFlag(CommonConst.FlagEnum.NOT.getId());
                }
            }
        } else {
            certFinallyStatus = CertConst.Status.NO_USE.getId();
        }
        // 如果原来有挂出 最后还是挂出
        if (CertConst.Status.BORROW_OUT.getId().equals(certInfoPo.getStatus())) {
            certFinallyStatus = CertConst.Status.BORROW_OUT.getId();
        }
        certInfoPo.setStatus(certFinallyStatus);

        transactionService.save(req);
        transactionService.update(borrowOutPo);
        certService.update(certInfoPo);
    }

    @Override
    @Transactional
    public void affiliationOutReturnBatchByCertIdList(List<CertTransactionInfoReq> reqList) {
        /*
         * 证书的批量还回操作
         */
        // 获取每个证书的对应记录
        // 获取最后一条挂出记录 由于有些事旧数据 兼容的情况下 需要对对应数据做出修复兼容
        List<Long> certificateIdList
                = reqList.stream()
                .map(CertTransactionInfoReq::getCertificateId)
                .collect(Collectors.toList());
        Map<String, Object> params = new HashMap<>();
        params.put("certificateIdList", certificateIdList);
        params.put("type", CertTransactionConst.Type.AFFILIATION_OUT.getId());
        //params.put("statusTag", CertTransactionConst.StatusTag.IN_HAND.getId());
        // 获取证书的借出记录进行分组
        Map<Long, CertiTransactionInfo> affiliationOutPoMapByCertId = transactionService.query(params)
                .stream()
                .collect(Collectors.toMap(CertiTransactionInfo::getCertificateId,
                        Function.identity(),
                        (affiliationOutPo1, affiliationOutPo2) -> {
                            if (affiliationOutPo1.getId() > affiliationOutPo2.getId()) {
                                return affiliationOutPo1;
                            } else {
                                return affiliationOutPo2;
                            }
                        }));

        // 验证是否都有挂出记录
        // 同时验证 只有 是挂出/借出人，或者挂出/借出人的上级 才能进行操作
        UserInfo userInfo = ShiroUtils.getUserEntity();
        CertiTransactionInfo returnFromTransactionInfoPo;
        List<Long> createByNotCurrentUserList = new ArrayList<>();
        for (CertTransactionInfoReq transactionReq : reqList) {
            if (!affiliationOutPoMapByCertId.containsKey(transactionReq.getCertificateId())) {
                throw new RRException(RRExceptionCodeEnum.CERT_CERTIFICATE_NO_AFFILIATION_RECORD);
            }
            returnFromTransactionInfoPo = affiliationOutPoMapByCertId.get(transactionReq.getCertificateId());
            if (!userInfo.getId().equals(transactionReq.getCertInfoPo().getCreateBy())) {
                createByNotCurrentUserList.add(returnFromTransactionInfoPo.getCreateBy());
            }
            transactionReq.setReturnFromTransactionInfoPo(returnFromTransactionInfoPo);
        }
        if (CollectionUtil.isNotEmpty(createByNotCurrentUserList)) {
            // 校验是是否有权限
            if (!userInfoService.isLowerLevelUserIdListOfManagerUser(createByNotCurrentUserList, userInfo)) {
                throw new RRException(RRExceptionCodeEnum.NO_AUTHORITY);
            }
        }
        // 准备批量修改内容
        List<CertiTransactionInfo> affiliationOutUpdateDoList = new ArrayList<>();
        List<Long> affiliationOutUpdateIdList = new ArrayList<>();
        List<CertificateInfo> certUpdateDoList = new ArrayList<>();
        CertiTransactionInfo affiliationOutUpdateDo;
        Date nowDate = new Date();
        List<CertiTransactionInfo> transactionSaveBatchDoList = new ArrayList<>();
        for (CertTransactionInfoReq transactionReq : reqList) {
            transactionReq.setType(CertTransactionConst.Type.AFFILIATION_OUT_RETURN.getId());
            // 基础信息配置
            transactionReq.setCreateBy(userInfo.getId());
            transactionReq.setCreateTime(nowDate);
            transactionReq.setTenantId(userInfo.getTenantId());
            // 默认收回时间
            if (ObjectUtil.isNull(transactionReq.getTransactionTime())) {
                transactionReq.setTransactionTime(nowDate);
                transactionReq.setTransactionEndTime(nowDate);
            }
            // 挂出记录中 需要修改的数据
            affiliationOutUpdateDo = transactionReq.getReturnFromTransactionInfoPo();
            affiliationOutUpdateDo.setStatusTag(CertTransactionConst.StatusTag.COMPLETION.getId());
            affiliationOutUpdateDoList.add(affiliationOutUpdateDo);
            affiliationOutUpdateIdList.add(affiliationOutUpdateDo.getId());
            // 将挂出记录复制到还回中
            transactionReq.copyAffiliationOutInfoToReturn(affiliationOutUpdateDo);
            CertificateInfo certInfoPo = transactionReq.getCertInfoPo();
            // 修改证书的状态
            if (CommonConst.FlagEnum.IS.getId().equals(certInfoPo.getBorrowOutFlag())) {
                // 获取最后一条借出未还回的记录，修改证书的状态
                params = new HashMap<>();
                params.put("certificateId", certInfoPo.getId());
                params.put("type", CertTransactionConst.Type.BORROW_OUT.getId());
                List<CertiTransactionInfo> borrowOutInfoPoList = transactionService.query(params);
                // 倒序重新排序一次
                borrowOutInfoPoList = CollectionUtil.sort(borrowOutInfoPoList, (po1, po2) -> (int) (po2.getId() - po1.getId()));
                if (CollectionUtil.isEmpty(borrowOutInfoPoList)) {
                    // 错误数据矫正
                    certInfoPo.setStatus(CommonConstants.CertificateStatus.NO_USE);
                    certInfoPo.setWarrantFlag(CommonConst.FlagEnum.NOT.getId());
                    certInfoPo.setBorrowOutFlag(CommonConst.FlagEnum.NOT.getId());
                } else {
                    CertiTransactionInfo borrowOutInfoPoLast = null;
                    for (CertiTransactionInfo borrowOutInfoPo : borrowOutInfoPoList) {
                        if (ObjectUtil.isNull(borrowOutInfoPo.getStatusTag()) ||
                                CertTransactionConst.StatusTag.IN_HAND.getId().equals(borrowOutInfoPo.getStatusTag())) {
                            borrowOutInfoPoLast = borrowOutInfoPo;
                            break;
                        }
                    }
                    if (ObjectUtil.isNotNull(borrowOutInfoPoLast)) {
                        // 旧数据修复
                        if (ObjectUtil.isNull(borrowOutInfoPoLast.getStatusTag())) {
                            borrowOutInfoPoLast.setStatusTag(CertTransactionConst.StatusTag.IN_HAND.getId());
                            transactionService.update(borrowOutInfoPoLast);
                        }
                        certInfoPo.setStatus(CertConst.Status.getById(borrowOutInfoPoLast.getUsage()).getId());
                    } else {
                        // 错误数据矫正
                        certInfoPo.setStatus(CertConst.Status.NO_USE.getId());
                        certInfoPo.setWarrantFlag(CommonConst.FlagEnum.NOT.getId());
                        certInfoPo.setBorrowOutFlag(CommonConst.FlagEnum.NOT.getId());
                    }
                }
            } else {
                certInfoPo.setStatus(CertConst.Status.NO_USE.getId());
            }
            certUpdateDoList.add(certInfoPo);
            transactionSaveBatchDoList.add(transactionReq);
        }

        // 对数据进行批量处理
        transactionService.saveBatch(transactionSaveBatchDoList);
        // 更新操作
        params = new HashMap<>();
        params.put("ids", affiliationOutUpdateIdList);
        params.put("statusTag", CertTransactionConst.StatusTag.COMPLETION.getId());
        transactionService.updateTransactionBatch(params);
        // 证书信息修改
        for (CertificateInfo certUpdateDo : certUpdateDoList) {
            certService.update(certUpdateDo);
        }
    }

    @Override
    public void borrowOutReturnBatchByCertIdList(CertTransactionInfoReq req) {
        /*
         * 批量还回证书，将所有证书还回
         * 还回证书下所有的借出流转记录
         */
        // 获取证书下借出记录
        List<Long> certificateIdList
                = req.getCertInfoPoList().stream()
                .map(CertificateInfo::getId)
                .collect(Collectors.toList());
        Map<String, Object> params = new HashMap<>();
        params.put("certificateIdList", certificateIdList);
        params.put("types", new Integer[]{
                CertTransactionConst.Type.BORROW_OUT.getId(),
                CertTransactionConst.Type.BORROW_OUT_RETURN.getId()});
        // 获取证书的借出记录进行分组 这里要把借出和还回的记录都拿出来，对数据进行原数据修复
        // 1-由于原先系统是押证的逻辑，需要根据每个证书的借出还回记录，进行判断原先的记录是否是还回了的，
        //              对对应的数据进行状态置空
        // 2-而后续多余的借出记录需要添加还回记录
        Map<Long, List<CertiTransactionInfo>> borrowOutAndReturnPoListGroupByCertId = transactionService.query(params)
                .stream().collect(Collectors.groupingBy(CertiTransactionInfo::getCertificateId));

        // 处理数据
        List<CertiTransactionInfo> borrowOutAndReturnRepairList = new ArrayList<>();// 需要修复的数据
        List<CertiTransactionInfo> borrowOutNotReturnList = new ArrayList<>();// 未还回的借出记录
        for (Map.Entry<Long, List<CertiTransactionInfo>> borrowOutAndReturnPoListGroupByCertIdEntry : borrowOutAndReturnPoListGroupByCertId.entrySet()) {
            List<CertiTransactionInfo> certBorrowOutAndReturnPoList = borrowOutAndReturnPoListGroupByCertIdEntry.getValue();
            certBorrowOutAndReturnPoList
                    = CollectionUtil.sort(certBorrowOutAndReturnPoList,
                    (po1, po2) -> (int) (po2.getId() - po1.getId()));
            // 统计借出和还回的数量
            int borrowOutRecordAmount = 0;
            int borrowOutReturnRecordAmount = 0;
            for (CertiTransactionInfo certBorrowOutAndReturnPo : certBorrowOutAndReturnPoList) {
                if (CertTransactionConst.Type.BORROW_OUT.getId().equals(certBorrowOutAndReturnPo.getType())) {
                    borrowOutRecordAmount++;
                } else {
                    borrowOutReturnRecordAmount++;
                }
            }
            int borrowOutAmount = borrowOutRecordAmount - borrowOutReturnRecordAmount;

            List<CertiTransactionInfo> certBorrowOutAndReturnRepairList = new ArrayList<>();
            List<CertiTransactionInfo> certBorrowOutNotReturnList = new ArrayList<>();
            for (CertiTransactionInfo certBorrowOutAndReturnPo : certBorrowOutAndReturnPoList) {
                if (CertTransactionConst.Type.BORROW_OUT.getId().equals(certBorrowOutAndReturnPo.getType())
                        && borrowOutAmount > 0 && certBorrowOutNotReturnList.size() < borrowOutAmount) {
                    // 这里是需要还回的数据
                    certBorrowOutNotReturnList.add(certBorrowOutAndReturnPo);
                    // borrowOutNotReturnList.addAll(certBorrowOutAndReturnPoList.subList(0, borrowOutAmount));
                } else {
                    certBorrowOutAndReturnRepairList.add(certBorrowOutAndReturnPo);
                }
            }
            if (CollectionUtil.isNotEmpty(certBorrowOutAndReturnRepairList)) {
                borrowOutAndReturnRepairList.addAll(certBorrowOutAndReturnRepairList);
            }
            if (CollectionUtil.isNotEmpty(certBorrowOutNotReturnList)) {
                borrowOutNotReturnList.addAll(certBorrowOutNotReturnList);
            }
        }

        // 修复数据
        if (CollectionUtil.isNotEmpty(borrowOutAndReturnRepairList)) {
            List<Long> transactionRepairIdList = borrowOutAndReturnRepairList
                    .stream()
                    .filter(po -> !CertTransactionConst.StatusTag.COMPLETION.getId().equals(po.getStatusTag()))
                    .map(CertiTransactionInfo::getId)
                    .collect(Collectors.toList());
            if (CollectionUtil.isNotEmpty(transactionRepairIdList)) {
                // 将旧的数据 完成标签修改为完成
                params = new HashMap<>();
                params.put("ids", transactionRepairIdList);
                params.put("statusTag", CertTransactionConst.StatusTag.COMPLETION.getId());
                transactionService.updateTransactionBatch(params);
            }
        }

        // 重新分组 需要还回的数据
        Map<Long, List<CertiTransactionInfo>> borrowOutListGroupByCertId = borrowOutNotReturnList.stream()
                .collect(Collectors.groupingBy(CertiTransactionInfo::getCertificateId));

        // 同时验证 只有 是挂出/借出人，或者挂出/借出人的上级 才能进行操作
        UserInfo userInfo = ShiroUtils.getUserEntity();
        List<Long> createByNotCurrentUserList = new ArrayList<>();
        List<CertTransactionInfoReq> transactionReqList = new ArrayList<>();
        CertTransactionInfoReq transactionReq;
        Date nowDate = new Date();
        for (CertificateInfo certPo : req.getCertInfoPoList()) {
            if (!borrowOutListGroupByCertId.containsKey(certPo.getId())) {
                throw new RRException(RRExceptionCodeEnum.CERT_CERTIFICATE_NO_BORROW_OUT_RECORD);
            }

            List<CertiTransactionInfo> returnFromTransactionInfoPoList = borrowOutListGroupByCertId.get(certPo.getId());
            for (CertiTransactionInfo returnFromTransactionInfoPo : returnFromTransactionInfoPoList) {
                if (!userInfo.getId().equals(certPo.getCreateBy())) {
                    createByNotCurrentUserList.add(returnFromTransactionInfoPo.getCreateBy());
                }
                transactionReq = new CertTransactionInfoReq();
                transactionReq.setCertificateId(certPo.getId());
                transactionReq.setRemark(req.getRemark());
                if (ObjectUtil.isNull(req.getTransactionEndTime())) {
                    transactionReq.setTransactionTime(nowDate);
                    transactionReq.setTransactionEndTime(nowDate);
                }
                transactionReq.setReturnFromId(returnFromTransactionInfoPo.getId());
                transactionReq.setReturnFromTransactionInfoPo(returnFromTransactionInfoPo);
                transactionReqList.add(transactionReq);
            }
        }
        // 权限校验
        if (CollectionUtil.isNotEmpty(createByNotCurrentUserList)) {
            // 校验是是否有权限
            if (!userInfoService.isLowerLevelUserIdListOfManagerUser(createByNotCurrentUserList, userInfo)) {
                throw new RRException(RRExceptionCodeEnum.NO_AUTHORITY);
            }
        }

        // 准备批量修改内容
        List<CertiTransactionInfo> borrowOutUpdateDoList = new ArrayList<>();
        List<Long> borrowOutUpdateIdList = new ArrayList<>();
        CertiTransactionInfo borrowOutUpdateDo;
        // 批量处理内容
        List<CertiTransactionInfo> transactionSaveBatchDoList = new ArrayList<>();
        for (CertTransactionInfoReq transactionInfoReq : transactionReqList) {
            // 基础信息配置
            transactionInfoReq.setCreateBy(userInfo.getId());
            transactionInfoReq.setCreateTime(nowDate);
            transactionInfoReq.setTenantId(userInfo.getTenantId());
            // 修改对应借出记录信息
            borrowOutUpdateDo = transactionInfoReq.getReturnFromTransactionInfoPo();
            borrowOutUpdateDo.setStatusTag(CertTransactionConst.StatusTag.COMPLETION.getId());
            borrowOutUpdateDo.setTransactionEndTime(req.getTransactionEndTime());
            if (ObjectUtil.isNull(borrowOutUpdateDo.getStatusTag())) {
                // 旧数据自修复
                borrowOutUpdateDo.setDetaineeFlag(CommonConst.FlagEnum.NOT.getId());
                borrowOutUpdateDo.setWarrantFlag(CommonConst.FlagEnum.IS.getId());
            }
            borrowOutUpdateDoList.add(borrowOutUpdateDo);
            borrowOutUpdateIdList.add(borrowOutUpdateDo.getId());
            transactionInfoReq.copyBorrowOutInfoToReturn(borrowOutUpdateDo);
            transactionSaveBatchDoList.add(transactionInfoReq);
        }

        // 对数据进行批量处理
        // 保存
        transactionService.saveBatch(transactionSaveBatchDoList);
        // 更新操作
        params = new HashMap<>();
        params.put("ids", borrowOutUpdateIdList);
        params.put("statusTag", CertTransactionConst.StatusTag.COMPLETION.getId());
        transactionService.updateTransactionBatch(params);

        for (CertificateInfo certificateInfo : req.getCertInfoPoList()) {
            // 如果证书不是挂出，那么证书最后为空闲状态
            if (!CertConst.Status.BORROW_OUT.getId().equals(certificateInfo.getStatus())) {
                certificateInfo.setStatus(CertConst.Status.NO_USE.getId());
            }
            certificateInfo.setWarrantFlag(CommonConst.FlagEnum.NOT.getId());
            certificateInfo.setBorrowOutFlag(CommonConst.FlagEnum.NOT.getId());
            certService.update(certificateInfo);
        }
    }
}
